import { isObject } from "@vue/shared";
import { cloneDeep } from "lodash-es";

/**
 * @description 添加单位
 * @param {String | Number} value 值 100
 * @param {String} unit 单位 px em rem
 */
export const addUnit = (value: string | number, unit = "px") => {
    return !Object.is(Number(value), NaN) ? `${value}${unit}` : value;
};

/** 判断是否为空
 * @description 判断是否为null undefined
 * @param {unknown} value
 * @return {Boolean}
 */
export const isEmpty = (value: unknown) => {
    return value == null && typeof value == "undefined";
};

/**
 * @description 树转数组，队列实现广度优先遍历
 * @param {Array} data  数据
 * @param {Object} props `{ children: 'children' }`
 */

export const treeToArray = (data: any[], props = { children: "children" }) => {
    data = cloneDeep(data);
    const { children } = props;
    const newData = [];
    const queue: any[] = [];
    data.forEach((child: any) => queue.push(child));
    while (queue.length) {
        const item: any = queue.shift();
        if (item[children]) {
            item[children].forEach((child: any) => queue.push(child));
            delete item[children];
        }
        newData.push(item);
    }
    return newData;
};

/**
 * @description 数组转
 * @param {Array} data  数据
 * @param {Object} props `{ parent: 'pid', children: 'children' }`
 */

export const arrayToTree = (data: any[], props = { id: "id", parentId: "pid", children: "children" }) => {
    data = cloneDeep(data);
    const { id, parentId, children } = props;
    const result: any[] = [];
    const map = new Map();
    data.forEach(item => {
        map.set(item[id], item);
        const parent = map.get(item[parentId]);
        if (parent) {
            parent[children] = parent[children] ?? [];
            parent[children].push(item);
        } else {
            result.push(item);
        }
    });
    return result;
};

/**
 * @description 获取正确的路经
 * @param {String} path  数据
 */
export function getNormalPath(path: string) {
    if (path.length === 0 || !path || path == "undefined") {
        return path;
    }
    const newPath = path.replace("//", "/");
    const length = newPath.length;
    if (newPath[length - 1] === "/") {
        return newPath.slice(0, length - 1);
    }
    return newPath;
}

/**
 * @description 对象格式化为Query语法
 * @param { Object } params
 * @return {string} Query语法
 */
export function objectToQuery(params: Record<string, any>): string {
    let query = "";
    for (const props of Object.keys(params)) {
        const value = params[props];
        const part = encodeURIComponent(props) + "=";
        if (!isEmpty(value)) {
            if (isObject(value)) {
                for (const key of Object.keys(value)) {
                    if (!isEmpty(value[key])) {
                        const params = props + "[" + key + "]";
                        const subPart = encodeURIComponent(params) + "=";
                        query += subPart + encodeURIComponent(value[key]) + "&";
                    }
                }
            } else {
                query += part + encodeURIComponent(value) + "&";
            }
        }
    }
    return query.slice(0, -1);
}

/**
 * @description 时间格式化
 * @param dateTime { number } 时间戳
 * @param fmt { string } 时间格式
 * @return { string }
 */
// yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合
export const timeFormat = (dateTime: number, fmt = "yyyy-mm-dd") => {
    // 如果为null,则格式化当前时间
    if (!dateTime) {
        dateTime = Number(new Date());
    }
    // 如果dateTime长度为10或者13，则为秒和毫秒的时间戳，如果超过13位，则为其他的时间格式
    if (dateTime.toString().length == 10) {
        dateTime *= 1000;
    }
    const date = new Date(dateTime);
    let ret;
    const opt: any = {
        "y+": date.getFullYear().toString(), // 年
        "m+": (date.getMonth() + 1).toString(), // 月
        "d+": date.getDate().toString(), // 日
        "h+": date.getHours().toString(), // 时
        "M+": date.getMinutes().toString(), // 分
        "s+": date.getSeconds().toString() // 秒
    };
    for (const k in opt) {
        ret = new RegExp("(" + k + ")").exec(fmt);
        if (ret) {
            fmt = fmt.replace(ret[1]!, ret[1]!.length == 1 ? opt[k] : opt[k].padStart(ret[1]!.length, "0"));
        }
    }
    return fmt;
};

/**
 * @description 获取不重复的id
 * @param length { Number } id的长度
 * @return { String } id
 */
export const getNonDuplicateID = (length = 8) => {
    let idStr = Date.now().toString(36);
    idStr += Math.random().toString(36).substring(3, length);
    return idStr;
};

/**
 * 计算颜色透明度减淡
 */
export const calcColor = (color: string, opacity: number): string => {
    // 规范化透明度值在 0 ~ 1 之间
    opacity = Math.min(1, Math.max(0, opacity));

    // 检查颜色是否是 hex 格式
    const isHex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
    const isRgb = /^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/;
    const isRgba = /^rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[0-9.]+\s*\)$/;

    let r: number = 0,
        g: number = 0,
        b: number = 0;

    if (isHex.test(color)) {
        // 如果是 hex 格式 (#ffffff 或 #fff)
        const hex = color.slice(1);

        // 如果是3位短格式，扩展为6位
        const fullHex =
            hex.length === 3
                ? hex
                      .split("")
                      .map(h => h + h)
                      .join("")
                : hex;

        // 转换为 RGB
        r = parseInt(fullHex.substring(0, 2), 16);
        g = parseInt(fullHex.substring(2, 4), 16);
        b = parseInt(fullHex.substring(4, 6), 16);
    } else if (isRgb.test(color)) {
        // 如果是 rgb 格式 (rgb(255, 255, 255))
        const rgbValues = color.match(/\d+/g);
        if (rgbValues) {
            r = parseInt(rgbValues[0]);
            g = parseInt(rgbValues[1]!);
            b = parseInt(rgbValues[2]!);
        }
    } else if (isRgba.test(color)) {
        // 如果是 rgba 格式 (rgba(255, 255, 255, 1))
        const rgbaValues = color.match(/\d+(\.\d+)?/g);
        if (rgbaValues) {
            r = parseInt(rgbaValues[0]);
            g = parseInt(rgbaValues[1]!);
            b = parseInt(rgbaValues[2]!);
        }
    } else {
        throw new Error("Unsupported color format");
    }

    // 返回转换后的 rgba 颜色值
    return `rgba(${r}, ${g}, ${b}, ${opacity})`;
};

/**
 * 递归遍历对象，移除值为 null、undefined 或空字符串 "" 的属性
 */
export const removeEmptyValue = (obj: Record<string, any>) => {
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            const value = obj[key];
            // 处理 null/undefined/空字符串
            if (value === null || value === undefined || value === "") {
                delete obj[key];
            } else if (Array.isArray(value)) {
                // 清理数组：递归处理并过滤空值
                const filtered = value
                    .map(item => (typeof item === "object" ? removeEmptyValue({ ...item }) : item))
                    .filter(item => !(item === null || item === undefined || item === ""));
                if (filtered.length > 0) obj[key] = filtered;
                else delete obj[key];
            } else if (typeof value === "object") {
                // 处理普通对象，递归后检查是否为空
                removeEmptyValue(value);
                if (Object.keys(value).length === 0) {
                    delete obj[key];
                }
            }
        }
    }
    return obj;
};

interface Config {
    /** 是否深度去除（默认 false） */
    deep?: boolean;
    /** 自定义判断空值的方法（可选） */
    isEmpty?: (value: unknown) => boolean;
}

/**
 * 移除对象中空属性（null/undefined/空字符串），支持深度处理
 * @param obj 要处理的对象
 * @param config 配置选项
 * @returns 清理后的新对象
 */
export function removeEmptyProperties<T extends Record<string, any>>(obj: T, config: Config = {}): Partial<T> {
    const { deep = false, isEmpty = defaultIsEmpty } = config;

    // 基础类型直接返回
    if (typeof obj !== "object" || obj === null) return obj;

    // 处理数组的情况（当遇到数组元素时）
    if (Array.isArray(obj)) {
        return deep ? (handleArray(obj, config) as unknown as Partial<T>) : ([...obj] as unknown as Partial<T>);
    }

    // 处理普通对象
    return Object.keys(obj).reduce((acc, key) => {
        const value = obj[key];

        // 判断是否为空值
        if (isEmpty(value)) return acc;

        // 处理深度模式
        const processedValue = deep ? deepProcess(value, config) : value;

        // 当处理后的值是空对象时自动排除
        if (isEmptyObject(processedValue)) return acc;

        return { ...acc, [key]: processedValue };
    }, {} as Partial<T>);
}

/** 默认空值判断逻辑 */
const defaultIsEmpty = (value: unknown): boolean => value === null || value === undefined || value === "";

/** 处理数组类型的值 */
const handleArray = (arr: any[], config: Config): any[] =>
    arr
        .map(item => deepProcess(item, config)) // 深度处理每个元素
        .filter(item => !config.isEmpty!(item)); // 过滤空元素

/** 深度处理值 */
const deepProcess = (value: any, config: Config): any => {
    if (typeof value !== "object" || value === null) return value;

    // 处理日期等特殊对象
    if (!isPlainObject(value) && !Array.isArray(value)) return value;

    return removeEmptyProperties(value, config);
};

/** 判断是否为纯对象 */
const isPlainObject = (obj: any): boolean => Object.prototype.toString.call(obj) === "[object Object]";

/** 判断是否为空对象 */
const isEmptyObject = (obj: any): boolean => isPlainObject(obj) && Object.keys(obj).length === 0;

type SizeUnit = "auto" | "B" | "KB" | "MB" | "GB" | "TB";

/**
 * 格式化文件大小（可控制单位）
 * @param bytes 文件大小（字节数）
 * @param decimals 保留的小数位数，默认为2
 * @param unit 单位，可选 "auto"（自动）、"B"（字节）、"KB"（千字节）、"MB"（兆字节）、"GB"（吉字节）、"TB"（太字节）
 * @returns 格式化后的字符串（如 "1.23 MB"）
 *
 * @example
 * formatFileSizeAdvanced(123456789); // "117.74 MB" (自动)
 * formatFileSizeAdvanced(123456789, 2, "KB"); // "120563.27 KB" (强制KB)
 * formatFileSizeAdvanced(123456789, 0, "GB"); // "0.11 GB" (强制GB)
 */
export function formatFileSizeAdvanced(bytes: number, decimals: number = 2, unit: SizeUnit = "auto"): string {
    if (bytes === 0) return "0 Bytes";

    const k = 1024;
    const sizes = ["Bytes", "KB", "MB", "GB", "TB"];

    let unitIndex: number;

    if (unit === "auto") {
        unitIndex = Math.floor(Math.log(bytes) / Math.log(k));
    } else {
        unitIndex = sizes.indexOf(unit);
        if (unitIndex === -1) unitIndex = 0;
    }

    // 确保单位索引在合理范围内
    unitIndex = Math.min(unitIndex, sizes.length - 1);

    const formattedValue = parseFloat((bytes / Math.pow(k, unitIndex)).toFixed(decimals));

    return `${formattedValue} ${sizes[unitIndex]}`;
}
